﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using MediaAssistant.DAL;
using MediaAssistant.Helper;
using MediaAssistant.MusicDJ;
using MefBasic.Extensions;

namespace MediaAssistant.Management
{
    [Export]
    public class SmartDJProbabilityCalculationService:BackgroundScanner
    {
        public SmartShuffleDJ SmartShuffleDj { get; private set; }
        public Music CurrentMusic { get; set; }

        public SmartDJProbabilityCalculationService()
        {
            Name = "Smart DJ Service";
        }
        protected override void TryToScan()
        {
            CancelIfRequested();
            if (PauseIfRequested())
            {
                return;
            }
            if (CurrentMusic == null)
                return;
            Calculate();
            CancelIfRequested();
        }

        private void CancelIfRequested()
        {
            if(CancelPendingJobRequested)
            {
                CurrentMusic = null;
                CancelPendingJobRequested = false;
            }
        }

        public void StartCalculation(Music playingMusic, SmartShuffleDJ smartShuffleDj)
        {
            if(CurrentMusic!=null)
                CancelPendingJob();
            SmartShuffleDj = smartShuffleDj;
            CurrentMusic = playingMusic;
        }

        private void Calculate()
        {
            var grandTotal = 0d;
            var genreDictionary = new Dictionary<string, double>();
            var albumDictionary = new Dictionary<string, double>();
            var artistDictionary = new Dictionary<string, double>();
            var composerDictionary = new Dictionary<string, double>();
            
            double totalLoop = SmartShuffleDj.NextMusics.Count*2;
            double currentLoop = 0;
            SetProbabilityCalculationProgress(totalLoop, currentLoop);
            foreach (var music in SmartShuffleDj.NextMusics)
            {
                music.TotalScore = DatabaseManager.Instance.GetScore(music, DataSource.Value.SelectedProfile);
                if (HasInfluenced(music.Genre, CurrentMusic.Genre) || HasInfluenced(music.Genre, SmartShuffleDj.InitializedMusic.Genre))
                {
                    string value = music.Genre;
                    var averageScore = genreDictionary.ContainsKey(value) ? genreDictionary[value] : GetAverageScore(SmartShuffleDj.AllMusics.Where(m => m.Genre == value), genreDictionary, value, DataSource.Value.SelectedProfile);
                    music.TotalScore += RegistryHelper.GenrePreferance * averageScore;
                }
                if (PauseIfRequested()) return;
                if (HasInfluenced(music.Album, CurrentMusic.Album))
                {
                    var value = music.Album;
                    var averageScore = albumDictionary.ContainsKey(value) ? albumDictionary[value] : GetAverageScore(SmartShuffleDj.AllMusics.Where(m => m.Album == value), albumDictionary, value, DataSource.Value.SelectedProfile);
                    music.TotalScore += RegistryHelper.AlbumPreferance * averageScore;
                }
                if (PauseIfRequested()) return;
                if (HasInfluenced(music.Artist, CurrentMusic.Artist))
                {
                    var value = music.Artist;
                    var averageScore = artistDictionary.ContainsKey(value) ? artistDictionary[value] : GetAverageScore(SmartShuffleDj.AllMusics.Where(m => m.Artist == value), artistDictionary, value, DataSource.Value.SelectedProfile);
                    music.TotalScore += RegistryHelper.ArtistPreferance * averageScore;
                }
                if (PauseIfRequested()) return;
                if (HasInfluenced(music.Composer, CurrentMusic.Composer))
                {
                    var value = music.Composer;
                    var averageScore = composerDictionary.ContainsKey(value) ? composerDictionary[value] : GetAverageScore(SmartShuffleDj.AllMusics.Where(m => m.Composer == value), composerDictionary, value, DataSource.Value.SelectedProfile);
                    music.TotalScore += RegistryHelper.ComposerPreferance * averageScore;
                }
                grandTotal += music.TotalScore;
                if (PauseIfRequested()) return;
                currentLoop++;
                SetProbabilityCalculationProgress(totalLoop,currentLoop);
            }
            var cumulativeProbability = 0d;
            foreach (var music in SmartShuffleDj.NextMusics)
            {
                music.Probability = music.TotalScore / grandTotal;
                cumulativeProbability += music.Probability;
                if (PauseIfRequested()) return;
                currentLoop++;
                SetProbabilityCalculationProgress(totalLoop, currentLoop);
            }
            SmartShuffleDj.SetMoveNext(cumulativeProbability);
            SmartShuffleDj.SetMovePrevious();
            CurrentMusic = null;
            SetStatusMessage("Ready");
        }

        private void SetProbabilityCalculationProgress(double totalLoop, double currentLoop)
        {
            SetStatusMessage(string.Format("Calculating Probability {0}%", Convert.ToInt32((currentLoop/totalLoop)*100)));
            Application.Current.DoEvents();
        }

        protected override bool PauseIfRequested()
        {
            return CancelPendingJobRequested || base.PauseIfRequested();
        }

        public static double GetAverageScore(IEnumerable<Music> influencedMusic, IDictionary<string, double> scoreDictionary, string value, Profile selectedProfile)
        {
            var averageScore = influencedMusic.Any() ? influencedMusic.Average(m => DatabaseManager.Instance.GetScore(m, selectedProfile)) : 0;
            scoreDictionary.Add(value, averageScore);
            return averageScore;
        }

        public static bool HasInfluenced(string value, string influencedBy)
        {
            return String.IsNullOrWhiteSpace(value) == false && String.Compare(value, influencedBy, StringComparison.OrdinalIgnoreCase) == 0;
        }
    }
}
